home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 3 / Cream of the Crop 3.iso / science / ack3d.zip / ACKVIEW.C < prev    next >
Text File  |  1994-01-09  |  17KB  |  599 lines

  1. /******************* ( Animation Construction Kit 3D ) ***********************/
  2. /*             Build Current Viewport                     */
  3. /* CopyRight (c) 1993       Author: Lary Myers                     */
  4. /*****************************************************************************/
  5.  
  6. #include <stdlib.h>
  7. #include <stdio.h>
  8. #include <dos.h>
  9. #include <mem.h>
  10. #include <alloc.h>
  11. #include <io.h>
  12. #include <fcntl.h>
  13. #include <time.h>
  14. #include <string.h>
  15. #include <sys\stat.h>
  16. #include "ack3d.h"
  17. #include "ackeng.h"
  18. #include "ackext.h"
  19.  
  20. void draw_col2(int Col,int slice,int dist,int width,int ht,UCHAR far *Wall,UCHAR far *Screen,UCHAR far *PalTable,int LightFlag,int hoff);
  21. void AckCopyBackground(UCHAR far *scrn,UCHAR far *bkgd,int len,int offset);
  22. void DrawSlices(ACKENG *ae);
  23.  
  24. void FindObject(int xPlayer,int yPlayer,int PlayerAngle,ACKENG *ae);
  25. UINT xRay(int x,int y,int angle,ACKENG *ae);
  26. UINT yRay(int x,int y,int angle,ACKENG *ae);
  27. int FindDoor(int MapPosn,ACKENG *ae);
  28. long long_sqrt(long v);
  29.  
  30. void CheckDoors(ACKENG *ae);
  31.  
  32. extern UCHAR    xLight;
  33. extern UCHAR    yLight;
  34.  
  35. /****************************************************************************
  36. ** This is the main routine the application should call to create the 3D   **
  37. ** view of the current POV. This routine will build all the walls and       **
  38. ** objects in the current view into the ScreenBuffer pointer within the       **
  39. ** ACKENG structure. No display to the video is done by this routine. The  **
  40. ** application can determine when and how actual display is to be done.       **
  41. **                                       **
  42. ****************************************************************************/
  43. int AckBuildView(ACKENG *ae)
  44. {
  45.         int        i,j,index,xPlayer,yPlayer,PlayerAngle;
  46.         UINT    xBitmap,yBitmap,BitmapNumber;
  47.         int        ViewAngle;
  48.         int        DoorOpenColumn;
  49.         long    xDistance,yDistance;
  50.         long    WallDistance;
  51.         int        distance,LightAdj;
  52.         UINT    BitmapColumn,yBitmapColumn;
  53.         int        OldMapPosn,OldMapPosn1;
  54.         long    xd,yd;
  55.         UINT    offset;
  56.  
  57. CheckDoors(ae);
  58. xPlayer = ae->xPlayer;
  59. yPlayer = ae->yPlayer;
  60. PlayerAngle = ae->PlayerAngle;
  61.  
  62. /* Begin looking 30 degrees to the left of our current angle */
  63. ViewAngle = PlayerAngle - INT_ANGLE_30;
  64.  
  65. if (ViewAngle < 0)
  66.     ViewAngle += INT_ANGLE_360;
  67.  
  68. MaxDistance = 0;
  69. LightAdj = 0;
  70.  
  71. /* Cast two rays for each column of the video display */
  72.  
  73. for (i = 0; i < VIEW_WIDTH; i++)
  74.     {
  75.     WallDistance = 3000000;    /* Set to a ridiculous distance */
  76.     BitmapColumn = -1;        /* Set to no walls found    */
  77.  
  78. /* Don't even cast an X ray if impossible to intersect the X walls */
  79.     if (ViewAngle != INT_ANGLE_90 && ViewAngle != INT_ANGLE_270)
  80.     {
  81.     BitmapNumber = xRay(xPlayer,yPlayer,ViewAngle,ae);
  82.  
  83.     if (BitmapNumber)    /* A wall was found */
  84.         {
  85.         /* Use the Y intercept to determine the wall column */
  86.         BitmapColumn = (LastY1 >> FP_SHIFT) & 0x3F;
  87.  
  88.         /* Keep the orientation the same no matter which side we're on */
  89.         if ((int)iLastX < xPlayer)
  90.         BitmapColumn = 63 - BitmapColumn;
  91.  
  92.         /* Did we strike a door? */
  93.         if ((BitmapNumber & 0xFF) == DOOR_XCODE)
  94.         {
  95.         index = FindDoor(xMapPosn,ae);
  96.         if (index >= 0)                /* This is a valid door */
  97.             {
  98.             j = ae->Door[index].ColOffset;  /* Get its current pos  */
  99.             offset = 0;
  100.  
  101.         if (BitmapNumber & DOOR_TYPE_SLIDE)
  102.             {
  103.             DoorOpenColumn = 63;
  104.             if ((int)iLastX > xPlayer)        /* Handle orientation   */
  105.             j = -j;
  106.             BitmapColumn += j;            /* Adjust column to show */
  107.             }
  108.  
  109.         if (BitmapNumber & DOOR_TYPE_SPLIT)
  110.             {
  111.             DoorOpenColumn = 31;
  112.             if (BitmapColumn < 32)
  113.             {
  114.             BitmapColumn += j;
  115.             if (BitmapColumn > 31)
  116.                 offset = 1;
  117.             }
  118.             else
  119.             {
  120.             BitmapColumn -= j;
  121.             if (BitmapColumn < 32)
  122.                 offset = 1;
  123.             }
  124.  
  125.             }
  126.  
  127.  
  128.             if (offset == 1 || BitmapColumn > 63)
  129.             {
  130.             /* Get the grid coordinates for this door */
  131.             OldMapPosn = ae->Door[index].mPos;
  132.             OldMapPosn1 = ae->Door[index].mPos1;
  133.  
  134.             /* Fake the engine into thinking no door is there */
  135.             ae->xGrid[OldMapPosn] = 0;
  136.             ae->xGrid[OldMapPosn1] = 0;
  137.  
  138.             /* Cast the ray to get walls beyond the door */
  139.             BitmapNumber = xRay(xPlayer,yPlayer,ViewAngle,ae);
  140.  
  141.             /* Put back the door codes if not fully open */
  142.             if (ae->Door[index].ColOffset < DoorOpenColumn)
  143.                 {
  144.                 ae->xGrid[OldMapPosn] = ae->Door[index].mCode;
  145.                 ae->xGrid[OldMapPosn1] = ae->Door[index].mCode1;
  146.                 }
  147.  
  148.             /* Calc the new bitmap column of wall behind door */
  149.             BitmapColumn = (LastY1 >> FP_SHIFT) & 0x3F;
  150.             if ((int)iLastX < xPlayer)
  151.                 BitmapColumn = 63 - BitmapColumn;
  152.             }
  153.             }
  154.         }
  155.  
  156.         /* Calculate the distance to the wall. Since the X position was  */
  157.         /* fixed to move 64 or -64 we can use it to determine the actual */
  158.         /* wall distance. The InvCosTable values were stored with a fixed */
  159.         /* point of 20 decimal places. At this time we'll knock off 14 of */
  160.         /* them so we can later multiply with a fixed point value of 16   */
  161.         xd = iLastX - xPlayer;
  162.         WallDistance = (xd * InvCosTable[ViewAngle]) >> 14;
  163.  
  164.         /* Still looking for the reason this may occur. But this check  */
  165.         /* will force the distance to a ridiculous value so no wall is  */
  166.         /* seen later on when the X and Y walls are compared.        */
  167.         if (WallDistance < 0)
  168.         WallDistance = 120000L;
  169.  
  170.         LightAdj = xLight;
  171.         }
  172.  
  173.     }
  174.  
  175. /* Don't cast a Y ray if its impossible to intercept any Y walls */
  176.     if (ViewAngle != 0 && ViewAngle != INT_ANGLE_180)
  177.     {
  178.     yBitmap = yRay(xPlayer,yPlayer,ViewAngle,ae);
  179.  
  180.     if (yBitmap)        /* A wall was found */
  181.         {
  182.  
  183.         /* Use the X intercept to determine the column of the bitmap */
  184.         yBitmapColumn = (LastX1 >> FP_SHIFT) & 0x3F;
  185.  
  186.         /* Handle orientation from either side of the wall */
  187.         if ((int)iLastY > yPlayer)
  188.         yBitmapColumn = 63 - yBitmapColumn;
  189.  
  190.         /* Did we strike a door? */
  191.         if ((yBitmap & 0xFF) == DOOR_YCODE)
  192.         {
  193.         index = FindDoor(yMapPosn,ae);
  194.         if (index >= 0)            /* This is a valid door */
  195.             {
  196.             /* Get the current door column offset */
  197.             j = ae->Door[index].ColOffset;
  198.             offset = 0;
  199.  
  200.             /* Deal with orientation */
  201.  
  202.         if (yBitmap & DOOR_TYPE_SLIDE)
  203.             {
  204.             DoorOpenColumn = 63;
  205.             if ((int)iLastY < yPlayer)
  206.             j = -j;
  207.             yBitmapColumn += j;
  208.             }
  209.  
  210.         if (yBitmap & DOOR_TYPE_SPLIT)
  211.             {
  212.             DoorOpenColumn = 31;
  213.             if (yBitmapColumn < 32)
  214.             {
  215.             yBitmapColumn += j;
  216.             if (yBitmapColumn > 31)
  217.                 offset = 1;
  218.             }
  219.             else
  220.             {
  221.             yBitmapColumn -= j;
  222.             if (yBitmapColumn < 32)
  223.                 offset = 1;
  224.             }
  225.             }
  226.  
  227.             /* If beyond width of bitmap than cast again */
  228.             if (offset == 1 || yBitmapColumn > 63)
  229.             {
  230.  
  231.             /* Get the yGrid coordinates for this door */
  232.             OldMapPosn = ae->Door[index].mPos;
  233.             OldMapPosn1 = ae->Door[index].mPos1;
  234.  
  235.             /* Fool the engine into thinking no door is there */
  236.             ae->yGrid[OldMapPosn] = 0;
  237.             ae->yGrid[OldMapPosn1] = 0;
  238.  
  239.             /* Cast again for walls beyond the door */
  240.             yBitmap = yRay(xPlayer,yPlayer,ViewAngle,ae);
  241.  
  242.             /* Put door code back if not fully open */
  243.             if (ae->Door[index].ColOffset < DoorOpenColumn)
  244.                 {
  245.                 ae->yGrid[OldMapPosn] = ae->Door[index].mCode;
  246.                 ae->yGrid[OldMapPosn1] = ae->Door[index].mCode1;
  247.                 }
  248.  
  249.             /* Get the bitmap column of wall beyond door */
  250.             yBitmapColumn = (LastX1 >> FP_SHIFT) & 0x3F;
  251.             if ((int)iLastY > yPlayer)
  252.                 yBitmapColumn = 63 - yBitmapColumn;
  253.  
  254.             }
  255.             }
  256.         }
  257.  
  258.  
  259.         /* Calculate the distance to the wall. Since the Y position was  */
  260.         /* fixed to move 64 or -64 we can use it to determine the actual */
  261.         /* wall distance. The InvSinTable values were stored with a fixed */
  262.         /* point of 20 decimal places. At this time we'll knock off 14 of */
  263.         /* them so we can later multiply with a fixed point value of 16   */
  264.         yd = iLastY - yPlayer;
  265.         yDistance = (yd * InvSinTable[ViewAngle]) >> 14;
  266.  
  267.         /* Don't know the reason but change negative value into ridiculous */
  268.         if (yDistance < 0)
  269.         yDistance = 120000L;
  270.  
  271.         /* At this point check the distance to the Y wall against the X */
  272.         /* wall to see which one is closer. The closer one is the one   */
  273.         /* we'll draw at this column of the screen.                     */
  274.         if (yDistance < WallDistance)
  275.         {
  276.         WallDistance = yDistance;
  277.         BitmapNumber = yBitmap;
  278.         BitmapColumn = yBitmapColumn;
  279.         LightAdj = yLight;
  280.         }
  281.  
  282.         }
  283.  
  284.     }
  285.  
  286.     if (BitmapColumn < 64)    /* A wall was found (either X or Y) */
  287.     {
  288.  
  289.     /* To avoid a fishbowl affect we need to adjust the distance so */
  290.     /* it appears perpendicular to the center point of the display    */
  291.     /* which is relative angle 0 from the players current angle. We */
  292.     /* started at -30 degrees for the first screen column and will    */
  293.     /* cycle from -30 down to 0 then back up to +30 degrees. This    */
  294.     /* cosine value was pre-calculated and placed in ViewCosTable.    */
  295.     WallDistance *= ViewCosTable[i];
  296.  
  297.     /* Now we strip off somemore decimal points and check round-up    */
  298.     xd = WallDistance >> 14;
  299.     if (WallDistance - (xd << 14) >= 8096)
  300.         xd++;
  301.  
  302.     /* The last decimal points from the multiplication after the X and */
  303.     /* Y rays is stripped off and checked for round-up.           */
  304.     WallDistance = xd >> 6;
  305.     if (xd - (WallDistance << 6) >= 32)
  306.         WallDistance++;
  307.  
  308.     /* Don't really need to, but put it into an integer for fast compare */
  309.     distance = WallDistance;
  310.  
  311.     /* This is an arbitrary minimum distance to look for */
  312.     if (distance < 10)
  313.         distance = 10;
  314.  
  315.     /* Don't want it to go outside our table boundaries */
  316.     if (distance >= MAX_DISTANCE)
  317.         distance = MAX_DISTANCE - 1;
  318.  
  319.     /* Save the wall data to display when done with entire screen */
  320.     Walls[i].Distance = distance;
  321.     Walls[i].Number = BitmapNumber & 0xFF;
  322.     Walls[i].Column = BitmapColumn;
  323.     Walls[i].LightAdj = LightAdj;
  324.  
  325.     if (distance > MaxDistance)
  326.         MaxDistance = distance;
  327.     }
  328.  
  329.     ViewAngle++;
  330.     if (ViewAngle >= INT_ANGLE_360)
  331.     ViewAngle -= INT_ANGLE_360;
  332.  
  333.     }
  334.  
  335. DrawSlices(ae);
  336.  
  337. /* Now we look at any objects that may be closer than the walls */
  338. FindObject(xPlayer,yPlayer,PlayerAngle,ae);
  339.  
  340. return(0);
  341. }
  342.  
  343. /****************************************************************************
  344. ** Internal routine used to draw the walls, called by AckDrawView().       **
  345. **                                       **
  346. ****************************************************************************/
  347. void DrawSlices(ACKENG *ae)
  348. {
  349.     int        i,x1,wt,ht,LightFlag,hoff;
  350.     UCHAR   far *wall,*ScreenBuffer;
  351.     UCHAR   far *pTable;
  352.     UCHAR   far **bmaps;
  353.  
  354. AckCopyBackground(ae->ScreenBuffer,ae->BkgdBuffer,ae->WinLength,ae->WinStartOffset);
  355.  
  356. x1 = ae->WinEndX;
  357.  
  358. ht = ae->CenterRow;
  359. wt = BYTES_PER_ROW;
  360. hoff = ht * wt;
  361. ScreenBuffer = ae->ScreenBuffer;
  362. bmaps = ae->bMaps;
  363. pTable = ae->PalTable;
  364. LightFlag = ae->LightFlag;
  365.  
  366. for (i = ae->WinStartX; i < x1; i++)
  367.     {
  368.     wall = AckGetBitmapPtr(Walls[i].Number,bmaps);
  369.     draw_col2(i,Walls[i].Column,Walls[i].Distance,wt,ht,wall,
  370.           ScreenBuffer,pTable,LightFlag,hoff);
  371.     }
  372. }
  373.  
  374.  
  375. /****************************************************************************
  376. ** Internal routine used to determine which objects are visible in the       **
  377. ** current POV. There is still one major anomaly with this routine in that **
  378. ** "ghost" objects tend to appear at certain angles and distances. Any       **
  379. ** thoughts/comments on why this occurs would be greatly appreciated!       **
  380. **                                       **
  381. ****************************************************************************/
  382. void FindObject(int xPlayer,int yPlayer,int PlayerAngle,ACKENG *ae)
  383. {
  384.     int        i,j,count,SaveCenter;
  385.     int        ObjX,ObjY,ObjNum;
  386.     int        NewX,NewY,LightFlag;
  387.     int        MaxOpp,Column,ColBeg,ColEnd;
  388.     int        wt,ObjIndex,CenterColumn;
  389.     int        vidwt,vidht,hoff;
  390.     long    deltax,deltay;
  391.     long    xp,yp,distance;
  392.     long    SinValue,CosValue;
  393.     UCHAR   far *wall,*ScreenBuffer;
  394.     UCHAR   far *pTable;
  395.     UCHAR   far **omaps;
  396.  
  397.     char    mBuf[40];
  398.  
  399. vidht = ae->CenterRow;
  400. vidwt = BYTES_PER_ROW;
  401. hoff = vidht * vidwt;
  402. ScreenBuffer = ae->ScreenBuffer;
  403. omaps = ae->oMaps;
  404.  
  405.  
  406. TotalObjects = 0;
  407. ObjRelDist[0] = 0L;
  408. SinValue = SinTable[PlayerAngle];
  409. CosValue = CosTable[PlayerAngle];
  410.  
  411. /* First thing we'll do is check all the objects to see which ones may be */
  412. /* completely out of view, and to get some initial values for later...      */
  413. for (i = 0; i < MAX_OBJECTS; i++)
  414.     {
  415.     if (!ae->ObjList[i].Active)
  416.     continue;
  417.  
  418.     ObjX = ae->ObjList[i].x;
  419.     ObjY = ae->ObjList[i].y;
  420.  
  421. /* Translate the object coordinates to make relative to the POV */
  422.     NewX = ObjX - xPlayer;
  423.     NewY = ObjY - yPlayer;
  424.  
  425.     if (abs(NewX) < 10 && abs(NewY) < 10)
  426.     continue;
  427.  
  428.  
  429. #if 0
  430.     if (PlayerAngle > INT_ANGLE_180 && (NewY-63) > 0)
  431.     continue;
  432.  
  433.     if (PlayerAngle < INT_ANGLE_180 && (NewY+63) < 0)
  434.     continue;
  435.  
  436.     if (PlayerAngle > INT_ANGLE_270 || PlayerAngle < INT_ANGLE_90)
  437.     {
  438.     if ((NewX+63) < 0)
  439.         continue;
  440.     }
  441.  
  442.     if (PlayerAngle < INT_ANGLE_270 && PlayerAngle > INT_ANGLE_90)
  443.     {
  444.     if ((NewX-63) > 0)
  445.         continue;
  446.     }
  447. #endif
  448.  
  449.     if ((PlayerAngle == 0 || PlayerAngle == INT_ANGLE_180) && abs(NewX) < 2)
  450.     continue;
  451.  
  452.     if ((PlayerAngle == INT_ANGLE_90 || PlayerAngle == INT_ANGLE_270) &&
  453.     abs(NewY) < 2)
  454.     continue;
  455.  
  456.  
  457.  
  458. /* Rotate coordinates to current player angle */
  459.     xp = ((NewX * CosValue) + (NewY * SinValue)) >> FP_SHIFT;
  460.     yp = ((NewY * CosValue) - (NewX * SinValue)) >> FP_SHIFT;
  461.  
  462.     if (xp <= 0)
  463.     continue;
  464.  
  465.     distance = (long)long_sqrt((xp * xp) + (yp * yp));
  466.  
  467.     if (distance >= MAX_DISTANCE)
  468.     continue;
  469.  
  470. /* Place the objects in the correct order so further ones are behind */
  471.     j = TotalObjects;
  472.     if (j)
  473.     {
  474.     for (count = 0; count < TotalObjects; count++)
  475.         {
  476.         if (distance > ObjRelDist[count])
  477.         {
  478.         for (j = TotalObjects; j > count; j--)
  479.             {
  480.             ObjRelDist[j] = ObjRelDist[j-1];
  481.             ObjNumber[j]  = ObjNumber[j-1];
  482.             ObjDeltaX[j]  = ObjDeltaX[j-1];
  483.             ObjDeltaY[j]  = ObjDeltaY[j-1];
  484.             }
  485.  
  486.         j = count;
  487.         count = TotalObjects;
  488.         }
  489.         }
  490.     }
  491. /* Hold onto relavant data for the object found */
  492.     ObjNumber[j]  = i;
  493.     ObjRelDist[j] = distance;
  494.     ObjDeltaX[j] = xp;
  495.     ObjDeltaY[j] = yp;
  496.     TotalObjects++;
  497.     ObjRelDist[TotalObjects] = 0L;
  498.     }
  499.  
  500. /* Didn't find any objects on the above pass, so we're done */
  501. if (!TotalObjects)
  502.     return;
  503.  
  504. CenterColumn = ae->WinStartX + (ae->WinWidth / 2);
  505.  
  506. pTable = ae->PalTable;
  507. LightFlag = ae->LightFlag;
  508.  
  509. for (i = 0; i < TotalObjects; i++)
  510.     {
  511.     ObjIndex = ObjNumber[i];
  512.     ObjNum = ae->ObjList[ObjIndex].bmNum[ae->ObjList[ObjIndex].CurNum];
  513.  
  514.     ObjY = ae->ObjList[ObjIndex].y;
  515.     NewY = ObjY - yPlayer;
  516.  
  517.     distance = ObjRelDist[i];
  518.     deltax   = ObjDeltaX[i];
  519.     yp = deltay = ObjDeltaY[i];
  520.  
  521.     MaxOpp = ((LongTanTable[INT_ANGLE_30] * (long)deltax) >> FP_SHIFT);
  522.  
  523.     if (NewY < ObjY)
  524.     {
  525.     MaxOpp = -MaxOpp;
  526.     yp = -yp;
  527.     }
  528.  
  529.     if ((yp+32) < MaxOpp)
  530.     continue;
  531.  
  532.     Column = CenterColumn;
  533.  
  534.     if (MaxOpp)
  535.     Column = CenterColumn - ((deltay * CenterColumn) / MaxOpp);
  536.  
  537.     yp = ViewCosTable[Column];
  538.     if (yp)
  539.     {
  540.     distance = distance * yp;
  541.  
  542.     xp = distance >> 14;
  543.     if (distance - (xp << 14) >= 8096)
  544.         xp++;
  545.  
  546.     distance = xp;
  547.     }
  548.  
  549.     if (distance < 0)
  550.     continue;
  551.  
  552.     if (distance >= (MAX_DISTANCE - 10))
  553.     distance = MAX_DISTANCE-11;
  554.  
  555.     wt = DistanceTable[distance];
  556.  
  557. /* Keep the width of the object reasonable */
  558.  
  559.     if (wt > 300)
  560.     continue;
  561.  
  562.     if (wt < 16) wt = 16;
  563.  
  564.     yp = AdjustTable[distance];
  565.     xp = 0;            /* First col of the object to display */
  566.  
  567.     NewX = Column;
  568.  
  569.     if (ae->ObjList[ObjIndex].Sides)
  570.     ObjNum =
  571.      ae->ObjList[ObjIndex].bmNum[((PlayerAngle / ae->ObjList[ObjIndex].Sides) & 7)];
  572.  
  573.     SaveCenter = ae->CenterRow;
  574.     ae->CenterRow = ae->ObjList[ObjIndex].VidRow;
  575.     ColEnd = NewX + wt;
  576.  
  577.     for (Column = NewX - wt; Column < ColEnd; Column++)
  578.     {
  579.     if (Column >= ae->WinStartX && Column <= ae->WinEndX)
  580.         {
  581.         if (distance < (Walls[Column].Distance + 10))
  582.         {
  583.         wall = AckGetBitmapPtr(ObjNum,omaps);
  584.  
  585.         draw_col2(Column,xp >> FP_SHIFT,distance,vidwt,vidht,wall,
  586.               ScreenBuffer,pTable,LightFlag,hoff);
  587.         }
  588.  
  589.         }
  590.     xp += yp;   /* Advance the next column to display (scaling) */
  591.     }
  592.  
  593.     ae->CenterRow = SaveCenter;
  594.     }
  595.  
  596. }
  597.  
  598.  
  599.